package ar.uade.fain.ai.tpo.dao;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import ar.uade.fain.ai.tpo.model.Caracteristica;
import ar.uade.fain.ai.tpo.model.Ingrediente;
import ar.uade.fain.ai.tpo.model.Producto;
import ar.uade.fain.ai.tpo.model.Receta;
import ar.uade.fain.ai.tpo.model.RecetaCompuesta;
import ar.uade.fain.ai.tpo.model.RecetaSimple;

public class RecetaDAO extends CacheDAOSupport<Receta> {

	private static final String DELETE_INGREDIENTE = "UPDATE DELIVERY.DBO.INGREDIENTE  SET ACTIVO = 0 WHERE IDRECETA = ?";

	private static final String DELETE_RECETA = "UPDATE DELIVERY.DBO.RECETA SET ACTIVO = 0 WHERE IDRECETA = ?";

	private static final String INSERT_RECETA = "INSERT INTO DELIVERY.DBO.RECETA "
			+ "(idreceta,idcaracteristica,nombre,activo) VALUES (?,?,?,1)";

	private static final String GET_BY_ID = "SELECT "
			+ "	RC.IDRECETA, RC.NOMBRE, RC.IDCARACTERISTICA, "
			+ "	I.IDPRODUCTO, I.CANTIDAD "
			+ "FROM DELIVERY.DBO.RECETA RC "
			+ "LEFT JOIN DELIVERY.DBO.INGREDIENTE I ON I.IDRECETA = RC.IDRECETA "
			+ "WHERE RC.IDRECETA = ? AND RC.ACTIVO = 1 AND I.ACTIVO = 1";

	private static final String GET_NEXT_ID = "SELECT ISNULL(MAX(IDRECETA),0)+1 AS ID FROM DELIVERY.DBO.RECETA";

	private static final String GET_ID_RECETAS_COMPONENTES = "SELECT IDRECETA FROM RECETACOMPUESTA WHERE IDRECETACOMPUESTA = ?";

	private static final String INSERT_INGREDIENTE = "INSERT INTO DELIVERY.DBO.INGREDIENTE "
			+ "(idreceta,idproducto,cantidad,activo) values (?,?,?,1)";

	private ProductoDAO productoDAO;
	private CaracteristicaDAO caracteristicaDAO;

	public RecetaDAO() {
		super();
	}

	public ProductoDAO getProductoDAO() {
		return productoDAO;
	}

	public void setProductoDAO(ProductoDAO productoDAO) {
		this.productoDAO = productoDAO;
	}

	public CaracteristicaDAO getCaracteristicaDAO() {
		return caracteristicaDAO;
	}

	public void setCaracteristicaDAO(CaracteristicaDAO caracteristicaDAO) {
		this.caracteristicaDAO = caracteristicaDAO;
	}

	public Receta buscarPorId(final Long id) {
		Set<Receta> enCache = buscarEnCache(new Selector<Receta>() {
			@Override
			public boolean aceptar(Receta entidad) {
				return entidad.getId().equals(id);
			}
		});
		Receta result = null;
		if (enCache.isEmpty()) {
			result = load(id);
		} else {
			result = enCache.iterator().next();
		}
		return result;
	}

	private Receta load(final Long id) {
		Receta result;
		final List<Receta> matches = database().select(GET_BY_ID, id,
				new ResultSetExtractor<Receta>() {
					@Override
					public List<Receta> extraer(ResultSet rs)
							throws SQLException {
						List<Receta> list = new ArrayList<Receta>();
						List<Ingrediente> listIngrediente = new ArrayList<Ingrediente>();
						if (rs.next()) {
							
							Long idReceta = rs
									.getLong("idreceta");
							
							final List<Long> matches = database().select(
									GET_ID_RECETAS_COMPONENTES, idReceta, 
									new RowMapper<Long>() {
								@Override
								public Long map(ResultSet rs) throws SQLException {
									return rs.getLong("idreceta");
								}
							});
							
							
							if (matches.isEmpty()) {

								final RecetaSimple receta = new RecetaSimple(rs
										.getString("nombre"), rs
										.getLong("idreceta"));
								final Caracteristica caracteristica = caracteristicaDAO
										.buscarPorId(rs
												.getLong("idcaracteristica"));
								receta.setCaracteristica(caracteristica);
								long idProducto = rs.getLong("idproducto");
								if (!rs.wasNull()) {
									Producto producto = productoDAO
											.buscarPorId(idProducto);
									Ingrediente ingrediente = new Ingrediente();
									ingrediente.setCantidad(rs
											.getDouble("cantidad"));
									ingrediente.setReceta(receta);
									ingrediente.setProducto(producto);
									listIngrediente.add(ingrediente);
									while (rs.next()) {
										producto = productoDAO.buscarPorId(rs
												.getLong("idproducto"));
										ingrediente = new Ingrediente();
										ingrediente.setCantidad(rs
												.getDouble("cantidad"));
										ingrediente.setReceta(receta);
										ingrediente.setProducto(producto);
										listIngrediente.add(ingrediente);

									}
								}
								receta.setIngredientes(listIngrediente);
								list.add(receta);

							} else {

								final RecetaCompuesta receta = new RecetaCompuesta(rs
										.getString("nombre"), rs
										.getLong("idreceta"));
								final Caracteristica caracteristica = caracteristicaDAO
										.buscarPorId(rs
												.getLong("idcaracteristica"));
								receta.setCaracteristica(caracteristica);
								long idProducto = rs.getLong("idproducto");
								if (!rs.wasNull()) {
									Producto producto = productoDAO
											.buscarPorId(idProducto);
									Ingrediente ingrediente = new Ingrediente();
									ingrediente.setCantidad(rs
											.getDouble("cantidad"));
									ingrediente.setReceta(receta);
									ingrediente.setProducto(producto);
									listIngrediente.add(ingrediente);
									while (rs.next()) {
										producto = productoDAO.buscarPorId(rs
												.getLong("idproducto"));
										ingrediente = new Ingrediente();
										ingrediente.setCantidad(rs
												.getDouble("cantidad"));
										ingrediente.setReceta(receta);
										ingrediente.setProducto(producto);
										listIngrediente.add(ingrediente);

									}
									
								
									
									receta.setRecetas(multiLoad(matches));
									
									
								}
								receta.setIngredientes(listIngrediente);
								list.add(receta);

							}

						}
						return list;
					}
				});
		result = matches.isEmpty() ? null : matches.get(0);
		agregarACache(result);
		return result;
	}

	public void guardar(final Receta receta) {
		final Long nextId = database().selectNextId(GET_NEXT_ID);
		caracteristicaDAO.guardar(receta.getCaracteristica());
		database().insert(INSERT_RECETA, new ParameterSetter() {
			@Override
			public void setParametros(PreparedStatement statement)
					throws SQLException {
				statement.setLong(1, nextId);
				statement.setLong(2, receta.getCaracteristica().getId());
				statement.setString(3, receta.getNombre());
			}
		});
		receta.setId(nextId);
		insertIngredientes(receta);
		agregarACache(receta);
	}

	private void insertIngredientes(final Receta receta) {
		for (final Ingrediente ingrediente : receta.getIngredientes()) {
			database().insert(INSERT_INGREDIENTE, new ParameterSetter() {
				@Override
				public void setParametros(PreparedStatement statement)
						throws SQLException {
					statement.setLong(1, receta.getId());
					statement.setLong(2, ingrediente.getProducto().getId());
					statement.setDouble(3, ingrediente.getCantidad());
				}
			});
		}
	}

	public void eliminarReceta(final Long id) {
		final ParameterSetter idOnlySetter = ParameterSetter.Factory.idOnly(id);
		database().update(DELETE_RECETA, idOnlySetter);
		database().update(DELETE_INGREDIENTE, idOnlySetter);
		quitarDelCache(new Selector<Receta>() {
			@Override
			public boolean aceptar(final Receta entidad) {
				return entidad.getId().equals(id);
			}
		});

	}
	
	private List<Receta> multiLoad (List<Long> idsRecetas){
		List<Receta> recetas = new ArrayList<Receta>();
		
		for(int i = idsRecetas.size(); i>=0 ; i--){
			recetas.add(load(idsRecetas.get(i)));
		}
		
		return recetas;
	}

}
